package serie06.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class StdMoneyAmount extends StdStock<CoinTypes> 
								implements MoneyAmount {
	
	// CONSTRUCTEURS
	
	public StdMoneyAmount() {
		super(CoinTypes.class);
	}
	
    // REQUETES
    
    public int getAmount(CoinTypes c) {
    	if (c == null) {
    		throw new IllegalArgumentException("Type inexistant");
    	}
    	return getNumber(c) * c.getFaceValue();
    }
    
    public int getAmount() {
    	int accu = 0;
    	for (CoinTypes c : CoinTypes.values()) {
    		accu += getAmount(c);
    	}
    	return accu;
    }
   
    public MoneyAmount computeChange(int s) {
    	if (s < 0) {
    		throw new IllegalArgumentException("Somme negative");
    	}
    	if (s == 0) {
    		MoneyAmount m = new StdMoneyAmount();
    		return m;
    	}
    	
    	/*
    	 * Classe interne pour le backTracking
    	 */
    	class BackTracking {
    		
    		// ATTRIBUTS
    		// Entier representant la somme totale des pieces.
    		private int accuBT;
    		// Indique si le backtracking fournit une solution ou pas.
    		private boolean solBT;
    		// Liste de pieces qui se remplit pendant le backtracking.
    		private List<CoinTypes> listBT;
    		// Liste des pieces qui se trouvent dans le stock classee dans
    		//  l'ordre decroissant des valeurs des pieces.
    		private List<CoinTypes> listCoin;
    		
    		// CONSTRUCTEUR
    		
    		public BackTracking() {
    			accuBT = 0;
    			solBT = false;
    			listBT = new ArrayList<CoinTypes>();
    			listCoin = new ArrayList<CoinTypes>();
    			for (CoinTypes c : CoinTypes.values()) {
    		    	for (int i = 0; i < getNumber(c); i++) {
    		    		listCoin.add(0, c);
    		    	}
    		    }
    		}
    		
    		// REQUETES
    		
    		public boolean getSolBT() {
    			return solBT;
    		}
    		public List<CoinTypes> getListBT() {
    			return listBT;
    		}
    		
    		// COMMANDE
    		
    		public void computeBackTracking(int s, int choix) {
    			int index = choix; 
    		    while (!solBT && choix <= listCoin.size() - 1 
    		    			&& index <= listCoin.size() - 1) {
    		    	CoinTypes c = listCoin.get(index);
    		    	if (c.getFaceValue() + accuBT <= s) {
    		    		listBT.add(c);
    		    		accuBT += c.getFaceValue();
    		    		if (accuBT == s) {
    		    			solBT = true;
    		    		} else {
    		    			computeBackTracking(s, index + 1);
    		    			if (!solBT) {
    		    				accuBT -= c.getFaceValue();
    		    				listBT.remove(listBT.size() - 1);
    		    			}
    		    		}
    		    	}
    		    	index += 1;
    		    }
    		}
    	}
    	
    	BackTracking b = new BackTracking();
    	b.computeBackTracking(s, 0);
    	if (b.getSolBT()) {
    		// le backtracking fournit bien une solution
    		MoneyAmount m = new StdMoneyAmount();
    		Iterator<CoinTypes> it = b.getListBT().iterator();
    		while (it.hasNext()) {
    			m.addElement(it.next());
    		}
    		return m;
    	} else {
    		return null;
    	}
    }
    
    
    
    public MoneyAmount clone() {
    	MoneyAmount clone = (MoneyAmount) super.clone();
    	return clone;
    }
    
    // COMMANDES
    
    public void addAmount(MoneyAmount amount) {
    	if (amount == null) {
    		throw new IllegalArgumentException("Somme inexistante");
    	}
    	for (CoinTypes c : CoinTypes.values()) {
    		if (amount.getNumber(c) > 0) {
    			addElement(c, amount.getNumber(c));
    		}
    	}
    }
   
    public void removeAmount(MoneyAmount amount) {
    	if (amount == null) {
    		throw new IllegalArgumentException("Somme inexistante");
    	}
    	for (CoinTypes c : CoinTypes.values()) {
    		if (getNumber(c) < amount.getNumber(c)) {
    			throw new IllegalStateException("Trop de pieces à retirer");
    		}
    	}
    	for (CoinTypes c : CoinTypes.values()) {
    		if (amount.getNumber(c) > 0) {
    			removeElement(c, amount.getNumber(c));
    		}
    	}
    }
    
    // OUTILS
    
    

}
